<?php

/**
 * Application level controller base class.
 * Application-wide controller functionality can be implemented here. All
 * application controllers should be inherited from this class. This class
 * implements the Core_Defines interface.
 * @author Joona Somerkivi <joona.somerkivi@gmail.com>
 */
class Core_Controller implements Core_Defines {

    protected $request;
    protected $view;
    protected $allowForHttp = array();
    protected $skipAuthentication = array();

    public function __construct(Core_Request $request) {
        $this->view = new Core_View();
        $this->view->setBaseUri($request->getBaseUri());
        $this->setRequest($request);
        if(isset($this->inputValidationRules))
                $this->request->setValidationRules(
                    $this->inputValidationRules);
    }

    public function setRequest(Core_Request $request) {
        $this->request = $request;
    }

    public function __destruct() {
        $this->view->output();
        $this->request->resetValidationRules();
    }

    public function __toString() {
        return 'Controller ' . get_class();
    }

    public function commitAction($action, $isHttpRequest) {
        try {
            if (!method_exists($this, "_$action"))
                throw new Core_Controller_Exception("$this has no method '_$action'");
            else if ($isHttpRequest && !in_array("_$action", $this->allowForHttp))
                throw new Core_Controller_Exception("$this doesn't allow '_$action' for HTTP");
            else if ($isHttpRequest && !in_array("_$action", $this->skipAuthentication) && !Core_Auth::authenticate())
                throw new Core_Controller_Exception("$this authentication failed for action '_$action'");

            $this->{"_$action"}();
        } catch (Core_Controller_Exception $e) {
            Core_Logger::log($e->getMessage());
            $this->returnErrorMessage($e->getMessage());
        } catch (Exception $e1) {
            Core_Logger::log($e1->getMessage());
            $this->returnGenericError();
        }
    }

    /**
     * In case of unexpected exception thrown, return generic error
     * Sets $this->view->output
     * @param STRING $message Message to the error-log
     */
    protected function returnGenericError($message = '') {
        $this->view->set( array(
            'success' => false,
            'message' => 'Internal error occurred'
        ));
        Core_Logger::log($message, Core_Registry::get('ERROR_LOG'));
    }

    /**
     * Return message about unsuccessfull action
     * Sets $this->view->output
     * @param $message Informatic string describing unsuccessfull action
     */
    protected function returnErrorMessage($message) {
        $this->view->set( array(
            'success' => false,
            'message' => $message
        ));
        Core_Logger::log($message, Core_Registry::get('ERROR_LOG'));
    }

    /**
     * Return message about successfull action
     * Sets $this->view->output
     * @param $message Informatic string describing successfull action
     */
    protected function returnSuccessMessage($message) {
        $this->view->set( array(
            'success' => true,
            'message' => $message
        ));
    }

    /**
     * alias for returnSuccessMessage()
     * Sets $this->view->output
     * @param $message Informatic string describing successfull action
     */
    protected function returnMessage($message) {
        $this->returnSuccessMessage($message);
    }

    /**
     * Rerturn data (and message if needed) to the front-end
     *
     * Calculates size of $data into 'total' field of output
     * Sets $this->view->output
     *
     * @param $data Array of arrays containing data
     * @param $message Possible string message to be presented to end user
     */
    protected function returnData($data, $message = null, $total = null)
    {
        $this->view->set(array
            (
            'success' => true,
            'message' => $message,
            'data' => $data,
            'total' => is_null($total) ? sizeof($data) : $total
        ));
    }

    /**
     * Return data to the front-end
     *
     * Used with pagin of queries with start and limit parameters
     * $total is the number of results if there would not been used limitations
     * Sets $this->view->output
     *
     * @param $data Array of arrays containing data
     * @param $total Number of total records
     */
    protected function returnPageOfData($data, $total) {
        $this->view->set( array(
            'success' => true,
            'message' => null,
            'data' => $data,
            'total' => $total
        ));
    }

    /**
     * Rename given indexes from given object/array
     *
     * Will only rename an index if it already exists in original
     * @param $original Object or array to rename indexes from
     * @param $renameBy Array of (oldKey => newKey) pairs to rename by
     * @return $target Object or array with given indexes renamed
     */
    protected static function renameProperties($original, $renameBy) {
        foreach ($renameBy as $old => $new) {
            if (isset($original->$old)) {
                $original->$new = $original->$old;
                unset($original->$old);
            } else if (isset($original[$old])) {
                $original[$new] = $original[$old];
                unset($original[$old]);
            }
        }
        return $original;
    }

    public function return404($msg) {
        //$this->view->setOutputType(Core_View::OUTPUT_TYPE_TEMPLATE);
        //$this->view->resetHeaders();
        //$this->view->setHeader('Status', '404');
        $this->view->setTemplate('404.tpl');
        $this->returnErrorMessage($msg);
        //$this->view->set('Name', 'Not found');
    }

    protected function postParamOrValue($param, $orValue = null)
    {
        $value = $this->request->getPostData($param);
        if(strlen($value) == 0)
            return $orValue;
        else
            return $value;
    }

    protected function getParamOrValue($param, $orValue = null)
    {
        $value = $this->request->getGetData($param);
        if(strlen($value) == 0)
            return $orValue;
        else
            return $value;
    }

    /**
     * Get the ID of the user from session
     * returns null if session not set
     * 
     * @return INT/NULL userId
     */
    protected static function getUserId()
    {
        if(isset($_SESSION) && isset($_SESSION['user']))
        {
            $user = unserialize($_SESSION['user']);
            return (int)$user['id'];
        }
        else
            return null;
    }
}

class Core_Controller_Exception extends Exception {

}